package com.ray.export.runner.exports;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.ray.export.empty.Empty;
import com.ray.export.runner.Runner;
import com.ray.export.runner.RunnerResult;
import com.ray.export.spring.ApplicationContextUtil;
import com.ray.export.strategy.data.DataModel;
import com.ray.export.strategy.exports.DataExport;
import com.ray.export.strategy.result.SuccessResult;
import com.ray.export.template.ExportTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author jumWang
 * @Description: 数据导出
 * @Class: DataExportRunner
 * @Package com.ray.runner.exports
 * @date 2019/11/27 16:13
 * @company <p>杭州传化陆鲸科技有限公司</p>
 * @updateRecord time(修改时间)  author(修改人)   desc(修改内容)
 */
@Slf4j
public class DataExportRunner implements Runner<ExportTemplate> {
    @Override
    public RunnerResult doRunner(ExportTemplate annotation, DataModel dataModel, RunnerResult perResult) {
        log.info("执行数据导出操作");
        Class<?> dataExportClass = annotation.dataExport();
        if (dataExportClass == Empty.class) {
            return new RunnerResult(Empty.class, dataModel,dataModel.getBeforeResult());
        }
        //判读配置是否符合规则
        if (!DataExport.class.isAssignableFrom(dataExportClass)) {
            log.error("数据导出配置错误");
            throw new RuntimeException("数据导出配置错误");
        }
        //获取执行对象
        DataExport dataExport = (DataExport) ApplicationContextUtil.getApplicationContext().getBean(dataExportClass);
        if (ObjectUtils.isEmpty(dataExport)) {
            log.error("获取执行对象异常[{}]", dataExportClass.getName());
            throw new RuntimeException("获取执行对象异常");
        }
        SuccessResult successResult = new SuccessResult();
        if (!dataModel.isAsynchronou()) {
            this.syncExports(dataModel, successResult, dataExport);
        } else {
            this.asyncExports(dataModel, successResult, dataExport);
        }
        return new RunnerResult(DataExport.class, dataModel, successResult);
    }

    //同步导出
    private void syncExports(DataModel dataModel, SuccessResult successResult, DataExport dataExport) {
        if (0 == dataModel.getCount()) {
            successResult.setStatus(false);
            successResult.setMsg("查询数据为空");
        } else {
            dataModel.getResponse().setContentType("application/vnd.ms-excel");
            dataModel.getResponse().setCharacterEncoding("UTF-8");
            dataModel.getResponse().addHeader("Content-Disposition", "attachment; filename=" + System.currentTimeMillis() + ".xlsx");
            //分页查询
            if(dataModel.getIsPaging()) {
                Long j = dataModel.getCount() % dataModel.getPageSize() == 0 ? dataModel.getCount() / dataModel.getPageSize() : dataModel.getCount() / dataModel.getPageSize() + 1;
                try {
                    ExcelWriter excelWriter = EasyExcel.write(dataModel.getResponse().getOutputStream(), dataModel.getDataClazz()).build();
                    WriteSheet writeSheet = EasyExcel.writerSheet().build();
                    for(int i=1; i<=j; i++) {
                        List<Object> queryResult = dataExport.pageDataQuery(dataModel.getExportQueryRequest(), i, dataModel.getPageSize());
                        if (ObjectUtils.isEmpty(queryResult) || CollectionUtils.isEmpty(queryResult)) {
                            log.error("分页查询失败");
                            throw new RuntimeException("分页查询失败");
                        }
                        excelWriter.write(queryResult, writeSheet);
                    }
                    excelWriter.finish();
                } catch (IOException e) {
                    successResult.setStatus(false);
                    successResult.setMsg("导出失败");
                    e.printStackTrace();
                }
            }else {
                List<Object> queryResult = dataExport.pageDataQuery(dataModel.getExportQueryRequest(), 1, dataModel.getCount().intValue());
                if (ObjectUtils.isEmpty(queryResult) || CollectionUtils.isEmpty(queryResult)) {
                    log.error("分页查询失败");
                    throw new RuntimeException("分页查询失败");
                }
                try {
                    if(HashMap.class != dataModel.getDataClazz()) {
                        if(!StringUtils.isEmpty(dataModel.getExportQueryRequest().getTemplatePath())) {
                            String templatePath = dataModel.getExportQueryRequest().getTemplatePath();
                            EasyExcel.write(dataModel.getResponse().getOutputStream(), dataModel.getDataClazz()).withTemplate(templatePath).sheet().doWrite(queryResult);
                        }else {
                            EasyExcel.write(dataModel.getResponse().getOutputStream(), dataModel.getDataClazz()).sheet().doWrite(queryResult);
                        }
                    }else {
                        List<List<String>> headList = headTransform(dataExport.head());
                        queryResult = dataTransform(queryResult);
                        if(!StringUtils.isEmpty(dataModel.getExportQueryRequest().getTemplatePath())) {
                            String templatePath = dataModel.getExportQueryRequest().getTemplatePath();
                            EasyExcel.write(dataModel.getResponse().getOutputStream()).withTemplate(templatePath).head(headList).sheet().doWrite(queryResult);
                        }
                        EasyExcel.write(dataModel.getResponse().getOutputStream()).head(headList).sheet().doWrite(queryResult);
                    }
                } catch (Exception e) {
                    successResult.setStatus(false);
                    successResult.setMsg("导出失败");
                    e.printStackTrace();
                }
            }
        }
    }

    //异步导出
    private void asyncExports(DataModel dataModel, SuccessResult successResult, DataExport dataExport) {
        OutputStream outputStream = null;
        String filePath = System.currentTimeMillis() + ".xlsx";
        try {
            outputStream = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //分页查询
        if(dataModel.getIsPaging()) {
            Long j = dataModel.getCount() % dataModel.getPageSize() == 0 ? dataModel.getCount() / dataModel.getPageSize() : dataModel.getCount() / dataModel.getPageSize() + 1;
            ExcelWriter excelWriter = EasyExcel.write(outputStream, dataModel.getDataClazz()).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            for(int i=1; i<=j; i++) {
                List<Object> queryResult = dataExport.pageDataQuery(dataModel.getExportQueryRequest(), i, dataModel.getPageSize());
                if (ObjectUtils.isEmpty(queryResult) || CollectionUtils.isEmpty(queryResult)) {
                    log.error("分页查询失败");
                    throw new RuntimeException("分页查询失败");
                }
                excelWriter.write(queryResult, writeSheet);
            }
            excelWriter.finish();
        }else {
            List<Object> queryResult = dataExport.pageDataQuery(dataModel.getExportQueryRequest(), 1, dataModel.getCount().intValue());
            if (ObjectUtils.isEmpty(queryResult) || CollectionUtils.isEmpty(queryResult)) {
                log.error("分页查询失败");
                throw new RuntimeException("分页查询失败");
            }
            EasyExcel.write(outputStream, dataModel.getDataClazz()).sheet().doWrite(queryResult);
        }
        dataModel.setTmpFilePath(filePath);
    }

    private List<Object> dataTransform(List<Object> dataList) throws Exception {
        List newDataList = new ArrayList<>();
        for(Object data : dataList) {
            Field[] fields = data.getClass().getDeclaredFields();
            List<Field> fieldList = new ArrayList<>(CollectionUtils.arrayToList(fields));
            fieldList.addAll(CollectionUtils.arrayToList(data.getClass().getSuperclass().getDeclaredFields()));
            Integer subSubListSize = 0;
            for(Field field : fieldList) {
                if("childList".equals(field.getName())) {
                    List subDataList = (List) getFieldValueByName(field.getName(), data);
                    if(!CollectionUtils.isEmpty(subDataList)) {
                        subSubListSize = subDataList.size();
                    }
                }
            }
            if(subSubListSize > 0) {
                for(int i=0; i<subSubListSize; i++) {
                    List newSubDataList = new ArrayList<>();
                    for(Field field : fieldList) {
                        if("childList".equals(field.getName())) {
                            List subDataList = (List) getFieldValueByName(field.getName(), data);
                            Field[] subFields = subDataList.get(i).getClass().getDeclaredFields();
                            for(Field subField : subFields) {
                                newSubDataList.add(getFieldValueByName(subField.getName(), subDataList.get(i)));
                            }
                        }else {
                            if(i > 0) {
                                newSubDataList.add("");
                            }else {
                                newSubDataList.add(getFieldValueByName(field.getName(), data));
                            }
                        }
                    }
                    newDataList.add(newSubDataList);
                }
            }else {
                List newSubDataList = new ArrayList<>();
                for(Field field : fields) {
                    newSubDataList.add(getFieldValueByName(field.getName(), data));
                }
                newDataList.add(newSubDataList);
            }
        }
        return newDataList;
    }

    private Object getFieldValueByName(String fieldName, Object o) throws Exception {
        String firstLetter = fieldName.substring(0, 1).toUpperCase();
        String getter = "get" + firstLetter + fieldName.substring(1);
        Method method = o.getClass().getMethod(getter, new Class[] {});
        Object value = method.invoke(o, new Object[] {});
        return value;
    }

    private List<List<String>> headTransform(List<String> orgList) {
        List<List<String>> headList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(orgList)) {
            for(String str : orgList) {
                List<String> subList = new ArrayList<>();
                subList.add(str);
                headList.add(subList);
            }
        }
        return headList;
    }

}
